#include "buffer.h"

/* * * Inline extern prototypes * * */

LSUP_Buffer *LSUP_buffer_new (const void *data, const size_t size);
LSUP_rc LSUP_buffer_init (
        LSUP_Buffer *buf, const size_t size, const void *data);
bool LSUP_buffer_eq (const LSUP_Buffer *buf1, const LSUP_Buffer *buf2);
int LSUP_buffer_cmp (const LSUP_Buffer *buf1, const LSUP_Buffer *buf2);


/* * * API * * */

LSUP_rc
LSUP_buffer_init (LSUP_Buffer *buf, const size_t size, const void *data)
{
    // If size is zero, addr becomes NULL.
    void *tmp = realloc (buf->addr, size);
    if (UNLIKELY (size > 0 && tmp == NULL)) return LSUP_MEM_ERR;

    buf->addr = tmp;
    buf->size = size;

    if (data) memcpy (buf->addr, data, buf->size);

    return LSUP_OK;
}


void LSUP_buffer_print (const LSUP_Buffer *buf)
{
    for (size_t i = 0; i < buf->size; i++) {
        char chr = ((char*)buf->addr)[i];
        if (isprint (chr)) {
            fputc (chr, stdout);
        } else {
            printf ("\\x%02x", chr);
        }
    }
    printf ("\n");
}


char *
LSUP_buffer_as_str (const LSUP_Buffer *buf)
{
    size_t i, str_size = 1; // terminating NUL

    // Calculate alloc size first.
    for (i = 0; i < buf->size; i++) {
        char chr = ((char*)buf->addr)[i];
        if (isprint (chr)) str_size ++;
        else str_size += 4; // 4 characters for ASCII representation (\xNN).
    }

    log_trace ("Byte buffer str size: %lu", str_size);

    char *cstr = malloc (str_size);

    size_t cur = 0; // Position in target string.
    for (i = 0; i < str_size - 1; i++) {
        char chr = ((char*)buf->addr)[i];

        if (isprint (chr)) {
            cstr[cur] = chr;
            cur ++;
        } else {
            sprintf (cstr + cur, "\\x%02x", chr);
            cur += 4;
        }
    }

    cstr[str_size - 1] = 0; // terminating NUL.

    return cstr;
}


void LSUP_buffer_done (LSUP_Buffer *buf)
{
    if (LIKELY (buf)) free (buf->addr);
}

void LSUP_buffer_free (LSUP_Buffer *buf)
{
    LSUP_buffer_done (buf);
    free (buf);
}


/*
 * Buffer triples.
 */

LSUP_BufferTriple *
LSUP_btriple_new(LSUP_Buffer *s, LSUP_Buffer *p, LSUP_Buffer *o)
{
    LSUP_BufferTriple *sspo = malloc (sizeof (*sspo));
    if (!sspo) return NULL;

    if (UNLIKELY (LSUP_btriple_init (sspo, s, p, o))) {
        free (sspo);
        return NULL;
    }

    return sspo;
}


LSUP_rc
LSUP_btriple_init (
        LSUP_BufferTriple *sspo, LSUP_Buffer *s, LSUP_Buffer *p, LSUP_Buffer *o)
{
    sspo->s = s;
    sspo->p = p;
    sspo->o = o;

    return LSUP_OK;
}


void
LSUP_btriple_done (LSUP_BufferTriple *sspo)
{
    if (UNLIKELY (!sspo)) return;

    LSUP_buffer_done (sspo->s);
    LSUP_buffer_done (sspo->p);
    LSUP_buffer_done (sspo->o);
}


void
LSUP_btriple_free (LSUP_BufferTriple *sspo)
{
    if (UNLIKELY (!sspo)) return;

    LSUP_buffer_free (sspo->s);
    LSUP_buffer_free (sspo->p);
    LSUP_buffer_free (sspo->o);

    free (sspo);
}


void
LSUP_btriple_free_shallow (LSUP_BufferTriple *sspo)
{
    if (UNLIKELY (!sspo)) return;

    sspo->s->addr = sspo->p->addr = sspo->o->addr = NULL;
    LSUP_btriple_free (sspo);
}



/* Extern inline prototypes. */

LSUP_Key LSUP_buffer_hash (const LSUP_Buffer *buf);
LSUP_Key LSUP_btriple_hash (const LSUP_BufferTriple *strp);
LSUP_Buffer *LSUP_btriple_pos (const LSUP_BufferTriple *trp, LSUP_TriplePos n);
